#include <drivers/gpio.h>
#include <list.h>
#include <mm/mem.h>
#include <kernel/syslog.h>
#include <lib/eb_string.h>


static struct gpio_desc *gpio_active = NULL;


static void gpio_set_pin_busy(uint8_t *bit_map, uint16_t pin, uint16_t pin_max, bool flag)
{
    if(pin > pin_max) {
        SYSLOG_ERROR("pin is invalid");
        return;
    }

    uint8_t *index_byte = (bit_map + pin / 8);
    uint8_t index_bit = pin & 0x07;

    if(flag) {
        *index_byte |= (1 << index_bit);
    }
    else {
        *index_byte &= ~(1 << index_bit);
    }
}


static int gpio_get_pin_busy(uint8_t *bit_map, uint16_t pin, uint16_t pin_max)
{
    bool status = true;
    if(pin > pin_max) {
        SYSLOG_ERROR("pin is invalid");
        return -1;
    }

    uint8_t index_byte = *(bit_map + pin / 8);
    uint8_t index_bit = pin & 0x07;

    status = index_byte & (1 << index_bit);

    return status;
}


static int gpio_request_pin(struct gpio_desc *desc, uint16_t pin)
{
    if(gpio_get_pin_busy(desc->map, pin, desc->npins)) {
        SYSLOG_ERROR("pin [%d] is using, request failed", pin);
        return -1;
    }

    gpio_set_pin_busy(desc->map, pin, desc->npins, true);
    return 0;
}


static void gpio_free_pin(struct gpio_desc *desc, uint16_t pin)
{
    gpio_set_pin_busy(desc->map, pin, desc->npins, false);
}


int gpio_desc_register(struct gpio_desc *desc)
{
    desc->map = kmalloc(desc->npins / 8);
    if(desc->map == NULL) {
        SYSLOG_ERROR("prictrl request bit map failed");
        return -1;
    }

    eb_memset(desc->map, 0, desc->npins / 8);

    desc->ops->request_pin = gpio_request_pin;
    desc->ops->free_pin = gpio_free_pin;

    gpio_active = desc;
    return 0;
}


int gpio_request(uint16_t pin)
{
    if(gpio_active == NULL) {
        SYSLOG_ERROR("GPIO request failed: gpio controller not found\n");
        return -1;
    }

    if(pin > gpio_active->npins) {
        SYSLOG_ERROR("GPIO request failed: pin number is out of range\n");
        return -1;
    }
    
    return gpio_active->ops->request_pin(gpio_active, pin);
}


void gpio_set_value(uint16_t pin, uint8_t value)
{
    if(gpio_active == NULL) {
        SYSLOG_ERROR("GPIO request failed: gpio controller not found\n");
        return;
    }

    if(pin > gpio_active->npins) {
        SYSLOG_ERROR("GPIO request failed: pin number is out of range\n");
        return;
    }
    
    gpio_active->ops->set_value(pin, value);
}


void gpio_toggle_value(uint16_t pin)
{
    gpio_active->ops->set_value(pin, !gpio_active->ops->get_value(pin));
}


int gpio_get_value(uint16_t pin)
{
    return gpio_active->ops->get_value(pin);
}


int gpio_direction_intput(uint16_t pin)
{
    if(gpio_active == NULL) {
        SYSLOG_ERROR("GPIO request failed: gpio controller not found\n");
        return -1;
    }

    if(pin > gpio_active->npins) {
        SYSLOG_ERROR("GPIO request failed: pin number is out of range\n");
        return -1;
    }
    
    gpio_active->ops->set_dir(pin, false);
    return 0;
}


int gpio_direction_output(uint16_t pin, uint8_t value)
{
    if(gpio_active == NULL) {
        SYSLOG_ERROR("GPIO request failed: gpio controller not found\n");
        return -1;
    }

    if(pin > gpio_active->npins) {
        SYSLOG_ERROR("GPIO request failed: pin number is out of range\n");
        return -1;
    }
    
    gpio_active->ops->set_dir(pin, true);
    gpio_active->ops->set_value(pin, value);
    return 0;
}


void gpio_free(uint16_t pin)
{
    if(gpio_active == NULL) {
        SYSLOG_ERROR("GPIO request failed: gpio controller not found\n");
        return;
    }

    if(pin > gpio_active->npins) {
        SYSLOG_ERROR("GPIO request failed: pin number is out of range\n");
        return;
    }
    
    gpio_active->ops->free_pin(gpio_active, pin);
}


int gpio_to_irq(uint16_t pin, int flag)
{
    if(gpio_active == NULL) {
        SYSLOG_ERROR("GPIO request failed: gpio controller not found\n");
        return -1;
    }

    if(pin > gpio_active->npins) {
        SYSLOG_ERROR("GPIO request failed: pin number is out of range\n");
        return -1;
    }

    return gpio_active->ops->set_exti(pin, flag);
}


int gpio_mux_func(const struct gpio_conf *func, uint16_t npins)
{
    for(int i = 0; i < npins; i++) {
        if(gpio_request_pin(gpio_active, func[i].pin)) {
            SYSLOG_ERROR("pin mux function failed");
            return -1;
        }

        gpio_active->ops->set_mux(func[i].pin, func[i].mux);

        if(func[i].dir != IO_INVALID_DIR) {
            gpio_active->ops->set_dir(func[i].pin, func[i].dir);
        }

        gpio_active->ops->set_drv(func[i].pin, func[i].drv);

        if(func[i].dir != IO_INVALID_VAL) {
            gpio_active->ops->set_value(func[i].pin, func[i].val);
        }
    }

    return 0;
}


void gpio_mux_free(const struct gpio_conf *func, uint16_t npins)
{
    for(int i = 0; i < npins; i++) {
        gpio_free_pin(gpio_active, func[i].pin);

        gpio_active->ops->set_mux(func[i].pin, 0);
        gpio_active->ops->set_dir(func[i].pin, 0);
        gpio_active->ops->set_drv(func[i].pin, 0);
        gpio_active->ops->set_value(func[i].pin, 0);
    }
}
